package tech.waterism.topo.handler;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import tech.waterism.topo.context.CalculationContext;
import tech.waterism.topo.model.*;

import java.util.*;

import static org.junit.jupiter.api.Assertions.*;

/**
 * JointDispatchHandler 单元测试
 * 
 * @author waterism
 * @since 1.0.0
 */
class JointDispatchHandlerTest {
    
    private JointDispatchHandler handler;
    private CalculationContext context;
    
    @BeforeEach
    void setUp() {
        handler = new JointDispatchHandler();
        context = new CalculationContext();
    }
    
    @Test
    void testShouldTrigger_NoJointGroups() {
        // 准备：没有联调组
        context.setJointGroups(new ArrayList<>());
        
        // 执行
        boolean result = handler.shouldTrigger(context);
        
        // 验证：不应该触发
        assertFalse(result);
    }
    
    @Test
    void testShouldTrigger_UpstreamNotReady() {
        // 准备：联调组的上游节点未全部计算完成
        JointDispatchGroup group = new JointDispatchGroup();
        group.setJmsid(1);
        group.setUpstreamNodes(Arrays.asList("NODE01", "NODE02"));
        
        context.setJointGroups(Collections.singletonList(group));
        context.getJointGroupMap().put(1, group);
        
        // 只缓存一个节点
        context.cacheResult("NODE01", new NodeResult("NODE01", new ArrayList<>()));
        
        // 执行
        boolean result = handler.shouldTrigger(context);
        
        // 验证：不应该触发
        assertFalse(result);
    }
    
    @Test
    void testShouldTrigger_AllUpstreamReady() {
        // 准备：联调组的所有上游节点都已计算完成
        JointDispatchGroup group = new JointDispatchGroup();
        group.setJmsid(1);
        group.setUpstreamNodes(Arrays.asList("NODE01", "NODE02"));
        
        context.setJointGroups(Collections.singletonList(group));
        context.getJointGroupMap().put(1, group);
        
        // 缓存所有上游节点
        context.cacheResult("NODE01", new NodeResult("NODE01", new ArrayList<>()));
        context.cacheResult("NODE02", new NodeResult("NODE02", new ArrayList<>()));
        
        // 执行
        boolean result = handler.shouldTrigger(context);
        
        // 验证：应该触发
        assertTrue(result);
        assertNotNull(context.getTriggeredGroup());
        assertEquals(1, context.getTriggeredGroup().getJmsid());
    }
    
    @Test
    void testGetBacktrackPosition() {
        // 准备
        JointDispatchGroup group = new JointDispatchGroup();
        group.setUpstreamNodes(Arrays.asList("NODE01", "NODE02", "NODE03"));
        
        context.getNodeIndexMap().put("NODE01", 5);
        context.getNodeIndexMap().put("NODE02", 3);
        context.getNodeIndexMap().put("NODE03", 8);
        
        // 执行：应该返回最小索引
        int position = handler.getBacktrackPosition(group, context);
        
        // 验证
        assertEquals(3, position);
    }
    
    @Test
    void testGetBacktrackPosition_EmptyUpstream() {
        // 准备：空的上游列表
        JointDispatchGroup group = new JointDispatchGroup();
        group.setUpstreamNodes(new ArrayList<>());
        
        // 执行
        int position = handler.getBacktrackPosition(group, context);
        
        // 验证：应该返回0
        assertEquals(0, position);
    }
    
    @Test
    void testInitializeUpstreamNodes() {
        // 准备：联调组和已缓存的上游结果
        List<TimeSeriesData> upstreamData = createTimeSeriesData(3);
        context.cacheResult("NODE01", new NodeResult("NODE01", upstreamData));
        
        TopoNode node1 = new TopoNode();
        node1.setStcd("NODE01");
        node1.setData(new ArrayList<>());
        
        TopoNode node2 = new TopoNode();
        node2.setStcd("NODE02");
        node2.setData(new ArrayList<>());
        
        JointDispatchGroup group = new JointDispatchGroup();
        group.setUpstreamNodes(Collections.singletonList("NODE01"));
        group.setNodes(Arrays.asList(node1, node2));
        
        // 执行
        handler.initializeUpstreamNodes(group, context);
        
        // 验证：NODE01应该复用缓存数据
        assertNotNull(node1.getData());
        assertEquals(upstreamData, node1.getData());
        
        // NODE02不应该被影响
        assertNotNull(node2.getData());
        assertTrue(node2.getData().isEmpty());
    }
    
    @Test
    void testNeedCalculateDischarge_WithStoreType() {
        // 准备：有蓄滞洪区类型的输入
        InputComponent input = new InputComponent();
        input.setArtp("STORE");
        
        TopoNode node = new TopoNode();
        node.setInputs(Collections.singletonList(input));
        
        // 执行
        boolean result = handler.needCalculateDischarge(node);
        
        // 验证
        assertTrue(result);
    }
    
    @Test
    void testNeedCalculateDischarge_WithAreaType() {
        // 准备：有区间面积类型的输入
        InputComponent input = new InputComponent();
        input.setArtp("X");
        
        TopoNode node = new TopoNode();
        node.setInputs(Collections.singletonList(input));
        
        // 执行
        boolean result = handler.needCalculateDischarge(node);
        
        // 验证
        assertTrue(result);
    }
    
    @Test
    void testNeedCalculateDischarge_NoSpecialType() {
        // 准备：没有特殊类型的输入
        InputComponent input = new InputComponent();
        input.setArtp("D");
        
        TopoNode node = new TopoNode();
        node.setInputs(Collections.singletonList(input));
        
        // 执行
        boolean result = handler.needCalculateDischarge(node);
        
        // 验证
        assertFalse(result);
    }
    
    @Test
    void testSumQPrep() {
        // 准备
        InputComponent input1 = new InputComponent();
        input1.setData(createTimeSeriesData(3));
        input1.getData().get(0).setQPrep(10.0);
        input1.getData().get(1).setQPrep(15.0);
        input1.getData().get(2).setQPrep(20.0);
        
        InputComponent input2 = new InputComponent();
        input2.setData(createTimeSeriesData(3));
        input2.getData().get(0).setQPrep(5.0);
        input2.getData().get(1).setQPrep(8.0);
        input2.getData().get(2).setQPrep(10.0);
        
        TopoNode node = new TopoNode();
        node.setData(createTimeSeriesData(3));
        
        // 执行
        handler.sumQPrep(node, Arrays.asList(input1, input2));
        
        // 验证：QPrep应该是累加的
        assertEquals(15.0, node.getData().get(0).getQPrep());
        assertEquals(23.0, node.getData().get(1).getQPrep());
        assertEquals(30.0, node.getData().get(2).getQPrep());
    }
    
    @Test
    void testGetUpstreamFromJointCache_FromJointCache() {
        // 准备：在联调缓存中
        List<TimeSeriesData> data = createTimeSeriesData(2);
        NodeResult result = new NodeResult("NODE01", data);
        context.getJointResultCache().put("NODE01", result);
        
        InputComponent input = new InputComponent();
        input.setArcd("NODE01");
        
        // 执行
        NodeResult retrieved = handler.getUpstreamFromJointCache(input, context);
        
        // 验证
        assertNotNull(retrieved);
        assertEquals("NODE01", retrieved.getStcd());
        assertEquals(data, retrieved.getData());
    }
    
    @Test
    void testGetUpstreamFromJointCache_FromMainCache() {
        // 准备：不在联调缓存，在主缓存
        List<TimeSeriesData> data = createTimeSeriesData(2);
        NodeResult result = new NodeResult("NODE01", data);
        context.cacheResult("NODE01", result);
        
        InputComponent input = new InputComponent();
        input.setArcd("NODE01");
        
        // 执行
        NodeResult retrieved = handler.getUpstreamFromJointCache(input, context);
        
        // 验证
        assertNotNull(retrieved);
        assertEquals("NODE01", retrieved.getStcd());
    }
    
    // ========== 辅助方法 ==========
    
    private List<TimeSeriesData> createTimeSeriesData(int count) {
        List<TimeSeriesData> dataList = new ArrayList<>();
        long now = System.currentTimeMillis();
        for (int i = 0; i < count; i++) {
            TimeSeriesData data = new TimeSeriesData();
            data.setDt(new Date(now + i * 3600000));
            data.setQSim(0.0);
            data.setQPrep(0.0);
            dataList.add(data);
        }
        return dataList;
    }
}

